package be.ulb.f203.projet.metier;

import be.ulb.f203.projet.tools.GraphAlgorithms;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;

/**
 *
 * @author Lam
 */
public class Graphe extends Observable {

    private Noeud racine;
    private List<Noeud> noeuds;
    private List<Arc> arcs;
    private Chemin plusCourtCheminAllNodes;
    private DijkstraResult plusCourtsChemins;
    /**
     * Crée un nouveau graphe vide.
     */
    public Graphe() {
        noeuds = new ArrayList();
        arcs = new ArrayList();
    }
    /**
     * Retourne la racine du graphe.
     * @return la racine du graphe.
     */
    public Noeud getRacine() {
        return racine;
    }
    /**
     * Change la racine du graphe.
     * @param racine la nouvelle racine du graphe.
     */
    public void setRacine(Noeud racine) {
        this.racine = racine;
        updateGrapheResults();
        setChanged();
        notifyObservers();
    }
    /**
     * Retourne la liste des noeuds constituant le graphe.
     * @return 
     */
    public List<Noeud> getNoeuds() {
        return noeuds;
    }
    /**
     * Change la liste des noeuds constituant le graphe.
     * @param noeuds la nouvelle liste des noeuds constituant le graphe.
     */
    public void setNoeuds(List<Noeud> noeuds) {
        this.noeuds = noeuds;
        setChanged();
        notifyObservers();
    }
    /**
     * Créer un arc entre deux noeuds.
     * @param keyFrom Valeur du noeud de départ.
     * @param keyTo Valeur du noeud d'arrivée.
     * @param poids Poids de l'arc.
     */
    public void creerArc(String keyFrom, String keyTo, double poids) {
        Noeud nodeFrom = find(keyFrom);
        Noeud nodeTo = find(keyTo);
        if (nodeFrom != null && nodeTo != null) {
            arcs.add(new Arc(nodeFrom, nodeTo, poids));
            nodeFrom.addVoisin(nodeTo);
            setChanged();
            notifyObservers();
        }
    }
    /**
     * Retourne l'arc reliant deux noeuds.
     * @param depart Noeud de départ.
     * @param arrivee Noeud d'arrivée.
     * @return L'arc reliant deux noeuds.
     */
    public Arc getArc(Noeud depart, Noeud arrivee) {
        Arc myArc = null;
        for (Arc arc : arcs) {
            if ((arc.getDepart().equals(depart) && arc.getArrivee().equals(arrivee)) || (arc.getDepart().equals(arrivee) && arc.getArrivee().equals(depart))) {
                myArc = arc;
                break;
            }
        }
        return myArc;
    }
    /**
     * Ajoute un noeud au graphe
     * @param n Noeud ajouté au graphe.
     */
    public void ajouterNoeud(Noeud n) {
        noeuds.add(n);
        setChanged();
        notifyObservers();
    }
    /**
     * Retourne le noeud correspondant à la valeur.
     * @param key valeur du noeud.
     * @return le noeud s'il est trouvée, null sinon.
     */
    public Noeud find(String key) {
        for (Noeud n : noeuds) {
            if (n.getValue().equals(key)) {
                return n;
            }
        }
        return null;
    }
    /**
     * Retourne l'ordre du graphe.
     * @return l'ordre du graphe.
     */
    public int getOrder() {
        return noeuds.size();
    }
    /**
     * Retourne le noeud en fonction de son index.
     * @param index l'index du noeud.
     * @return Le noeud s'il est trouvé. 
     */
    public Noeud getNoeud(int index) {
        return noeuds.get(index);
    }
    /**
     * Retourne la liste des arcs du graphe.
     * @return la liste des arcs du graphe.
     */
    public List<Arc> getArcs() {
        return arcs;
    }
    /**
     * Retourne le nombre d'arcs.
     * @return Le nombre d'arc.
     */
    public int getNbArcs() {
        return arcs.size();
    }
    /**
     * Retourne le chemin le plus court passant par tous les noeuds.
     * @return le chemin le plus court passant par tous les noeuds, null s'il n'existe pas.
     */
    public Chemin getPlusCourtCheminAllNodes() {
        return plusCourtCheminAllNodes;
    }
    /**
     * Change le chemin le plus court passant par tous les noeuds.
     * @param plusCourtChemin le nouveau chemin le plus court passant par tous les noeuds.
     */
    public void setPlusCourtCheminAllNodes(Chemin plusCourtChemin) {
        this.plusCourtCheminAllNodes = plusCourtChemin;
    }
    
    private void updateGrapheResults() {
        setPlusCourtCheminAllNodes(GraphAlgorithms.findPathAllNodes(this, racine));
        setPlusCourtsChemins(GraphAlgorithms.executeDijkstra(this, racine));
    }

    /**
     * Retourne les chemins les plus courts depuis la racine jusqu'au noeuds.
     * @return les chemins les plus courts depuis la racine jusqu'au noeuds.
     */
    public DijkstraResult getPlusCourtsChemins() {
        return plusCourtsChemins;
    }
    /**
     * Change les chemins les plus courts depuis la racine jusqu'au noeuds.
     * @param plusCourtsChemins les nouveaux chemins les plus courts depuis la racine jusqu'au noeuds.
     */
    public void setPlusCourtsChemins(DijkstraResult plusCourtsChemins) {
        this.plusCourtsChemins = plusCourtsChemins;
    }
}
